import { Injectable } from '@nestjs/common';
import * as fs from 'fs';
import * as path from 'path';
import { Logger } from '@nestjs/common';

// 日志级别枚举
export enum LogLevel {
  ERROR = 'ERROR',
  WARN = 'WARN', 
  INFO = 'INFO',
  DEBUG = 'DEBUG'
}

// 跟踪日志数据接口
export interface TrackingLogData {
  level: LogLevel;
  trackingId: string;
  url: string;
  ip: string;
  userAgent: string;
  sessionId: string;
  message: string;
  data?: any;
  timestamp: string;
}

// 业务闭环数据接口
export interface BusinessLoopData {
  trackingId: string;
  websiteId: number;
  url: string;
  ip: string;
  sessionId: string;
  step: 'SCRIPT_LOADED' | 'PAGEVIEW_SENT' | 'PAGEVIEW_RECEIVED' | 'PAGEVIEW_SAVED' | 'ERROR' | string;
  status: 'SUCCESS' | 'FAILED' | 'SKIPPED';
  message: string;
  data?: any;
  timestamp: string;
}

/**
 * 日志记录服务
 */
@Injectable()
export class TrackingLoggerService {
  private readonly logger = new Logger(TrackingLoggerService.name);
  private readonly logDir: string;
  private readonly trackingLogDir: string;
  private readonly businessLoopLogDir: string;
  private readonly logLevel: LogLevel;
  private readonly maxLogSize: number = 10 * 1024 * 1024; // 10MB
  private readonly logRotationInterval: number = 24 * 60 * 60 * 1000; // 24小时
  private lastRotationTime: number = Date.now();

  constructor() {
    // 从环境变量获取日志级别，默认为INFO
    const envLogLevel = process.env.TRACKING_LOG_LEVEL || 'INFO';
    this.logLevel = LogLevel[envLogLevel as keyof typeof LogLevel] || LogLevel.INFO;
    
    this.logDir = path.join(process.cwd(), 'logs');
    this.trackingLogDir = path.join(this.logDir, 'tracking');
    this.businessLoopLogDir = path.join(this.logDir, 'business-loop');
    
    this.ensureLogDirectories();
  }

  private ensureLogDirectories(): void {
    [this.logDir, this.trackingLogDir, this.businessLoopLogDir].forEach(dir => {
      if (!fs.existsSync(dir)) {
        fs.mkdirSync(dir, { recursive: true });
      }
    });
  }

  // 检查是否应该记录日志
  private shouldLog(level: LogLevel): boolean {
    const levelPriority = {
      [LogLevel.ERROR]: 0,
      [LogLevel.WARN]: 1,
      [LogLevel.INFO]: 2,
      [LogLevel.DEBUG]: 3
    };
    
    return levelPriority[level] <= levelPriority[this.logLevel];
  }

  // 获取日志文件路径
  private getLogFilePath(logDir: string, prefix: string): string {
    const date = new Date().toISOString().split('T')[0];
    return path.join(logDir, `${prefix}-${date}.log`);
  }

  // 写入日志文件
  private writeLog(logFilePath: string, logData: any): void {
    try {
      const logLine = JSON.stringify(logData) + '\n';
      fs.appendFileSync(logFilePath, logLine, 'utf8');
      
      // 检查日志文件大小，如果超过限制则轮转
      this.checkLogRotation(logFilePath);
    } catch (error) {
      console.error('写入日志文件失败:', error);
    }
  }

  // 检查日志轮转
  private checkLogRotation(logFilePath: string): void {
    try {
      const stats = fs.statSync(logFilePath);
      const now = Date.now();
      
      // 检查文件大小或时间间隔
      if (stats.size > this.maxLogSize || (now - this.lastRotationTime) > this.logRotationInterval) {
        this.rotateLogFile(logFilePath);
        this.lastRotationTime = now;
      }
    } catch (error) {
      // 文件不存在或其他错误，忽略
    }
  }

  // 轮转日志文件
  private rotateLogFile(logFilePath: string): void {
    try {
      const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
      const rotatedPath = `${logFilePath}.${timestamp}`;
      fs.renameSync(logFilePath, rotatedPath);
      
      // 保留最近7天的日志文件
      this.cleanOldLogs(path.dirname(logFilePath));
    } catch (error) {
      console.error('轮转日志文件失败:', error);
    }
  }

  // 清理旧日志文件
  private cleanOldLogs(logDir: string): void {
    try {
      const files = fs.readdirSync(logDir);
      const now = Date.now();
      const maxAge = 7 * 24 * 60 * 60 * 1000; // 7天
      
      files.forEach(file => {
        if (file.includes('.log.')) {
          const filePath = path.join(logDir, file);
          const stats = fs.statSync(filePath);
          
          if (now - stats.mtime.getTime() > maxAge) {
            fs.unlinkSync(filePath);
          }
        }
      });
    } catch (error) {
      console.error('清理旧日志文件失败:', error);
    }
  }

  // 记录跟踪日志 - 优化版
  logTracking(
    level: LogLevel,
    trackingId: string,
    url: string,
    ip: string,
    userAgent: string,
    sessionId: string,
    message: string,
    data?: any
  ): void {
    if (!this.shouldLog(level)) {
      return;
    }

    const logData: TrackingLogData = {
      level,
      trackingId,
      url,
      ip,
      userAgent,
      sessionId,
      message,
      data,
      timestamp: new Date().toISOString()
    };

    const logFilePath = this.getLogFilePath(this.trackingLogDir, 'tracking');
    this.writeLog(logFilePath, logData);
  }

  // 记录业务闭环日志 - 优化版
  logBusinessLoop(
    trackingId: string,
    websiteId: number,
    url: string,
    ip: string,
    sessionId: string,
    step: string,
    status: 'SUCCESS' | 'FAILED' | 'SKIPPED',
    message: string,
    data?: any
  ): void {
    // 只记录重要的业务闭环步骤
    const importantSteps = ['SCRIPT_LOADED', 'PAGEVIEW_SAVED', 'ERROR'];
    if (!importantSteps.includes(step) && status === 'SUCCESS') {
      return; // 跳过成功的非重要步骤
    }

    const logData: BusinessLoopData = {
      trackingId,
      websiteId,
      url,
      ip,
      sessionId,
      step,
      status,
      message,
      data,
      timestamp: new Date().toISOString()
    };

    const logFilePath = this.getLogFilePath(this.businessLoopLogDir, 'business-loop');
    this.writeLog(logFilePath, logData);
  }

  // 简化的日志方法 - 减少重复代码
  logScriptLoaded(trackingId: string, url: string, ip: string, userAgent: string, sessionId: string, data?: any): void {
    this.logBusinessLoop(trackingId, 0, url, ip, sessionId, 'SCRIPT_LOADED', 'SUCCESS', 'Script loaded successfully', data);
  }

  logPageviewSent(trackingId: string, url: string, ip: string, userAgent: string, sessionId: string, data?: any): void {
    this.logBusinessLoop(trackingId, 0, url, ip, sessionId, 'PAGEVIEW_SENT', 'SUCCESS', 'Pageview data sent to server', data);
  }

  logPageviewReceived(trackingId: string, url: string, ip: string, userAgent: string, sessionId: string, websiteId: number, data?: any): void {
    this.logBusinessLoop(trackingId, websiteId, url, ip, sessionId, 'PAGEVIEW_RECEIVED', 'SUCCESS', 'Pageview data received by server', data);
  }

  logPageviewSaved(trackingId: string, url: string, ip: string, userAgent: string, sessionId: string, websiteId: number, pageviewId: number, data?: any): void {
    this.logBusinessLoop(trackingId, websiteId, url, ip, sessionId, 'PAGEVIEW_SAVED', 'SUCCESS', 'Pageview data saved to database', { pageviewId, ...data });
  }

  logError(trackingId: string, url: string, ip: string, userAgent: string, sessionId: string, step: string, error: Error, data?: any): void {
    this.logTracking(LogLevel.ERROR, trackingId, url, ip, userAgent, sessionId, `Error in ${step}`, { error: error.message, stack: error.stack, ...data });
    this.logBusinessLoop(trackingId, 0, url, ip, sessionId, 'ERROR', 'FAILED', `Error in ${step}: ${error.message}`, { error: error.message, ...data });
  }

  logWarning(trackingId: string, url: string, ip: string, userAgent: string, sessionId: string, message: string, data?: any): void {
    this.logTracking(LogLevel.WARN, trackingId, url, ip, userAgent, sessionId, message, data);
  }

  logDebug(trackingId: string, url: string, ip: string, userAgent: string, sessionId: string, message: string, data?: any): void {
    this.logTracking(LogLevel.DEBUG, trackingId, url, ip, userAgent, sessionId, message, data);
  }

  /**
   * 记录信息日志
   */
  logInfo(
    trackingId: string,
    url: string,
    ip: string,
    userAgent: string,
    sessionId: string,
    type: string,
    data?: any
  ) {
    const logEntry = {
      timestamp: new Date().toISOString(),
      trackingId,
      url,
      ip,
      userAgent,
      sessionId,
      type,
      data
    };
    
    this.logger.log(`INFO [${type}] ${trackingId} - ${url} - ${sessionId}`);
    
    try {
      // 将信息日志写入文件
      const today = new Date().toISOString().split('T')[0];
      const logDir = path.join(process.cwd(), 'logs');
      
      if (!fs.existsSync(logDir)) {
        fs.mkdirSync(logDir, { recursive: true });
      }
      
      const logFile = path.join(logDir, `${today}.log`);
      fs.appendFileSync(logFile, JSON.stringify(logEntry) + '\n');
    } catch (error) {
      this.logger.error(`Failed to write info log: ${error.message}`, error.stack);
    }
  }

  // 获取今日统计
  getTodayStats(): { trackingLogs: number; businessLoopLogs: number; errorLogs: number; errorRate: number } {
    const today = new Date().toISOString().split('T')[0];
    const trackingLogFile = this.getLogFilePath(this.trackingLogDir, 'tracking');
    const businessLoopLogFile = this.getLogFilePath(this.businessLoopLogDir, 'business-loop');

    let trackingLogs = 0;
    let businessLoopLogs = 0;
    let errorLogs = 0;

    try {
      if (fs.existsSync(trackingLogFile)) {
        const content = fs.readFileSync(trackingLogFile, 'utf8');
        const lines = content.split('\n').filter(line => line.trim());
        trackingLogs = lines.length;
        
        // 统计错误日志
        errorLogs = lines.filter(line => {
          try {
            const logData = JSON.parse(line);
            return logData.level === 'ERROR';
          } catch {
            return false;
          }
        }).length;
      }

      if (fs.existsSync(businessLoopLogFile)) {
        const content = fs.readFileSync(businessLoopLogFile, 'utf8');
        const lines = content.split('\n').filter(line => line.trim());
        businessLoopLogs = lines.length;
      }
    } catch (error) {
      console.error('获取今日统计失败:', error);
    }

    const errorRate = trackingLogs > 0 ? (errorLogs / trackingLogs) * 100 : 0;

    return {
      trackingLogs,
      businessLoopLogs,
      errorLogs,
      errorRate: Math.round(errorRate * 100) / 100
    };
  }

  // 获取业务闭环记录
  getBusinessLoopByTrackingId(trackingId: string, date?: string): BusinessLoopData[] {
    const targetDate = date || new Date().toISOString().split('T')[0];
    const logFile = path.join(this.businessLoopLogDir, `business-loop-${targetDate}.log`);

    try {
      if (!fs.existsSync(logFile)) {
        return [];
      }

      const content = fs.readFileSync(logFile, 'utf8');
      const lines = content.split('\n').filter(line => line.trim());
      
      return lines
        .map(line => {
          try {
            return JSON.parse(line) as BusinessLoopData;
          } catch {
            return null;
          }
        })
        .filter(log => log && log.trackingId === trackingId)
        .sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
    } catch (error) {
      console.error('获取业务闭环记录失败:', error);
      return [];
    }
  }

  // 检查业务闭环完整性
  checkBusinessLoopIntegrity(trackingId: string, date?: string): {
    isComplete: boolean;
    totalSteps: number;
    missingSteps: string[];
    records: BusinessLoopData[];
  } {
    const records = this.getBusinessLoopByTrackingId(trackingId, date);
    const expectedSteps = ['SCRIPT_LOADED', 'PAGEVIEW_SENT', 'PAGEVIEW_RECEIVED', 'PAGEVIEW_SAVED'];
    const actualSteps = records.map(r => r.step);
    
    const missingSteps = expectedSteps.filter(step => !actualSteps.includes(step));
    const isComplete = missingSteps.length === 0 && records.every(r => r.status === 'SUCCESS');

    return {
      isComplete,
      totalSteps: records.length,
      missingSteps,
      records
    };
  }
} 